#include "TcpServer.h"
#include "IPoller.h"
#include "TcpConn.h"
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>

TcpServer::TcpServer(IPoller* poller) : listen_fd_(-1), poller_(poller) {
}

TcpServer::~TcpServer() {
    if (listen_fd_ != -1) {
        poller_->DelEvent(listen_fd_);
        close(listen_fd_);
    }
}

void TcpServer::Start(int port, NewConnCallback cb) {
    new_conn_cb_ = db;
    listen_fd_ = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_fd_ == -1) {
        std::cerr << "create socket failed" << std::endl;
        return;
    }

    sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(port);

    int opt = 1;
    if( setsockopt(listen_fd_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
        std::cerr << "setsockopt failed" << std::endl;
        close(listen_fd_);
        return;
    }

    if( setsockopt(listen_fd_, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) == -1) {
        std::cerr << "setsockopt failed" << std::endl;
        close(listen_fd_);
        return;
    }

    if (bind(listen_fd_, (sockaddr*)&addr, sizeof(addr)) == -1) {
        std::cerr << "bind failed" << std::endl;
        close(listen_fd_);
        return;
    }

    if (listen(listen_fd_, 10) == -1) {
        std::cerr << "listen failed" << std::endl;
        close(listen_fd_);
        return;
    }

    accept_handler_ = [this](int events) {
        HandleAccept(events);
    };
    poller_->AddEvent(listen_fd_, EPOLLIN, accept_handler_);
    std::cout << "Server start on port " << port << std::endl;
}

void TcpServer::HandleAccept(int events) {
    sockaddr_in client_addr{};
    socklen_t client_addr_len = sizeof(client_addr);
    int client_fd = accept(listen_fd_, (sockaddr*)&client_addr, &client_addr_len, SOCK_NONBLOCK);
    if (client_fd == -1) {
        std::cerr << "accept failed" << std::endl;
        return;
    }
    std::cout << "accept client fd: " << client_fd << std::endl;
    if(new_conn_cb_)
    {
        new_conn_cb_(std::make_shared<TcpConn>(client_fd, poller_));
    }
}